// -----------------------------------------------------------------------
//  <copyright file="RegisterCommand.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2024 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2024/12/19 11:24</last-date>
// -----------------------------------------------------------------------

using DaprPlus.Identity.Application;
using DaprPlus.Identity.Domain.Entities;
using DaprPlus.Identity.Domain.Services;
using DaprPlus.Security;

using Liuliu.Fsb.Services.Identity.Domain.Entities;
using Liuliu.Fsb.Services.Identity.Domain.IntegrationEvents;

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;

namespace Liuliu.Fsb.Services.Identity.Application.Identity.Users.Commands;

[AuthConfig(typeof(User), ResourceAccessType.Anonymous, "Register")]
public class RegisterCommand : IRequest<ApiResult>, ICommand
{
    public RegisterType RegisterType { get; set; }

    public string? UserName { get; set; }

    public string Password { get; set; } = null!;

    public string ConfirmPassword { get; set; } = null!;

    public string? Email { get; set; }

    public string? PhoneNumber { get; set; }

    public string? ValidateCode { get; set; }

    public string CaptchaId { get; set; } = null!;

    public string Captcha { get; set; } = null!;

    /// <summary>
    /// 注册推荐人
    /// </summary>
    public string? Recommend { get; set; }

    public string? RegisterIp { get; set; }
}

public class RegisterCommandHandler(IServiceProvider provider) : IRequestHandler<RegisterCommand, ApiResult>
{
    public Task<ApiResult> Handle(RegisterCommand request, CancellationToken token)
    {
        var result = request.RegisterType switch
        {
            RegisterType.UserName => RegisterByUserNameAsync(request, token),
            RegisterType.Email => RegisterByEmailAsync(request, token),
            RegisterType.PhoneNumber => RegisterByPhoneNumberAsync(request, token),
            _ => throw new NotSupportedException("注册时传入错误的注册类型")
        };
        return result;
    }

    private Task<ApiResult> RegisterByUserNameAsync(RegisterCommand request, CancellationToken token)
    {
        var user = new User
        {
            UserName = request.UserName!
        };
        return SaveUserAsync(request, user, token);
    }

    private Task<ApiResult> RegisterByEmailAsync(RegisterCommand request, CancellationToken token)
    {
        var user = new User()
        {
            UserName = request.Email!,
            Email = request.Email!,
            EmailConfirmed = true
        };
        return SaveUserAsync(request, user, token);
    }

    private Task<ApiResult> RegisterByPhoneNumberAsync(RegisterCommand request, CancellationToken token)
    {
        var user = new User()
        {
            UserName = request.PhoneNumber!,
            PhoneNumber = request.PhoneNumber!,
            PhoneNumberConfirmed = true
        };
        return SaveUserAsync(request, user, token);
    }

    private async Task<ApiResult> SaveUserAsync(RegisterCommand request, User user, CancellationToken token)
    {
        user.NickName = $"User_{new Random().NextLetterAndNumberString(8)}";
        user.CreateUserExtend(request.RegisterIp);
        var userManager = provider.GetRequiredService<UserManager<User>>();

        var registeredEvent = new UserCreatedIntegrationEvent([new Domain.IntegrationEvents.UserRecord(user.Id, user.UserName, user.NickName, user.IsLocked, request.Recommend)]);
        user.AddIntegrationEvent(registeredEvent);

        var identityResult = request.Password == null ? await userManager.CreateAsync(user)
            : await userManager.CreateAsync(user, request.Password);
        if (!identityResult.Succeeded)
        {
            return identityResult.ToApiResult();
        }

        return new ApiResult(ApiResultType.Success, "用户注册成功");
    }
}

public class RegisterCommandValidator : AbstractValidator<RegisterCommand>
{
    public RegisterCommandValidator(IServiceProvider provider)
    {
        var userRepository = provider.GetRepository<User>();
        When(m => m.RegisterType == RegisterType.UserName, () =>
        {
            RuleFor(m => m.UserName).NotEmpty().MustAsync(async (request, userName, token) =>
            {
                var userManager = provider.GetRequiredService<UserManager<User>>();
                userName = userManager.NormalizeName(userName);
                var spec = new UserByNormalizedNameSpec<User>(userName!);
                var flag = await userRepository.AnyAsync(spec, token);
                return !flag;
            }).WithMessage(m => $"用户名 {m.UserName} 已存在");
        });

        When(m => m.RegisterType == RegisterType.Email, () =>
        {
            RuleFor(m => m.Email).NotEmpty().EmailAddress().MustAsync(async (request, email, token) =>
            {
                var userManager = provider.GetRequiredService<UserManager<User>>();
                email = userManager.NormalizeEmail(email);
                var spec = new UserByNormalizedEmailSpec<User>(email);
                var flag = await userRepository.AnyAsync(spec, token);
                return !flag;
            }).WithMessage(m => $"Email为 {m.Email} 的用户已存在");
            var codeMsg = string.Empty;
            RuleFor(m => m.ValidateCode).NotEmpty().MustAsync(async (request, code, token) =>
            {
                var validateCodeService = provider.GetRequiredService<IValidateCodeService>();
                var result = await validateCodeService.ValidateAsync(request.Email!, ValidateCodeType.Register, code!,
                    token: token);
                if (result.IsSuccess()) return true;
                codeMsg = result.Message;
                return false;
            }).WithMessage(_ => codeMsg);
        });

        When(m => m.RegisterType == RegisterType.PhoneNumber, () =>
        {
            RuleFor(m => m.PhoneNumber).NotEmpty().Length(11)
                .Matches(@"^1[3-9]\d{9}$").WithMessage("手机号不是有效的中国大陆手机号")
                .MustAsync(async (request, phone, token) =>
                {
                    var spec = new UserByPhoneNumberSpec<User>(phone);
                    var flag = await userRepository.AnyAsync(spec, token);
                    return !flag;
                }).WithMessage(m => $"手机号为 {m.PhoneNumber} 的用户已存在");
            var codeMsg = string.Empty;
            RuleFor(m => m.ValidateCode).NotEmpty().MustAsync(async (request, code, token) =>
            {
                var validateCodeService = provider.GetRequiredService<IValidateCodeService>();
                var result = await validateCodeService.ValidateAsync(request.PhoneNumber!, ValidateCodeType.Register, code!,
                    token: token);
                if (result.IsSuccess()) return true;
                codeMsg = result.Message;
                return false;
            }).WithMessage(_ => codeMsg);
        });

        RuleFor(m => m.Password).NotEmpty().Equal(m => m.ConfirmPassword).WithMessage("密码与确认密码不匹配");

        RuleFor(m => m.Captcha).NotEmpty().Must((request, value) =>
        {
            var captchaService = provider.GetRequiredService<ICaptchaService>();
            var result = captchaService.Validate(request.CaptchaId, value);
            return result;
        }).WithMessage("验证码错误");
    }
}

/// <summary>
/// 表示用户注册方式的枚举
/// </summary>
public enum RegisterType
{
    /// <summary>
    /// 用户名注册
    /// </summary>
    UserName,

    /// <summary>
    /// 邮箱注册
    /// </summary>
    Email,

    /// <summary>
    /// 手机号注册
    /// </summary>
    PhoneNumber
}

